package org.microframework.cloud.gateway.filter;

import lombok.extern.slf4j.Slf4j;
import org.microframework.cloud.gateway.config.AuthConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Arrays;
import java.util.List;

/**
 * 认证过滤器
 * 检查请求是否带有认证信息
 */
@Slf4j
@Component
public class AuthenticationFilter implements GlobalFilter, Ordered {

    @Autowired
    private AuthConfig authConfig;
    
    // 默认白名单，如果配置中没有指定，则使用这些
    private final List<String> defaultWhiteList = Arrays.asList(
            "/auth/", 
            "/system/v3/api-docs", 
            "/system/swagger-ui", 
            "/system/doc.html",
            "/system/webjars",
            "/system/swagger-resources"
    );

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 如果认证被禁用，直接放行
        if (!authConfig.isEnabled()) {
            return chain.filter(exchange);
        }
        
        ServerHttpRequest request = exchange.getRequest();
        String path = request.getURI().getPath();
        
        // 检查是否是黑名单路径（黑名单优先级高于白名单）
        if (!authConfig.getBlackList().isEmpty() && 
            authConfig.getBlackList().stream().anyMatch(path::startsWith)) {
            return checkAuth(exchange, chain, path);
        }
        
        // 检查是否是白名单路径
        List<String> whiteList = !authConfig.getWhiteList().isEmpty() ? 
                                 authConfig.getWhiteList() : defaultWhiteList;
        
        if (whiteList.stream().anyMatch(path::startsWith)) {
            log.debug("白名单路径，跳过认证: {}", path);
            return chain.filter(exchange);
        }
        
        // 非白名单路径，检查认证
        return checkAuth(exchange, chain, path);
    }
    
    /**
     * 检查认证信息
     */
    private Mono<Void> checkAuth(ServerWebExchange exchange, GatewayFilterChain chain, String path) {
        // 检查是否带有认证信息
        HttpHeaders headers = exchange.getRequest().getHeaders();
        String authHeader = headers.getFirst(HttpHeaders.AUTHORIZATION);
        String tokenPrefix = authConfig.getTokenPrefix();
        
        if (authHeader == null || authHeader.isEmpty() || !authHeader.startsWith(tokenPrefix)) {
            log.warn("请求未包含有效的认证信息: {}", path);
            
            // 未认证，返回401
            ServerHttpResponse response = exchange.getResponse();
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
            
            // 返回JSON格式错误信息
            String responseBody = "{\"code\":401,\"message\":\"未授权，请先登录\",\"success\":false}";
            return response.writeWith(
                Mono.just(response.bufferFactory().wrap(responseBody.getBytes()))
            );
        }
        
        // 提取token（这里只是简单判断有无token，实际项目中需要进一步验证token有效性）
        String token = authHeader.substring(tokenPrefix.length());
        log.debug("请求包含认证token: {}, 路径: {}", token.substring(0, Math.min(10, token.length())) + "...", path);
        
        // 将用户信息放入请求头中，传递给下游服务
        ServerHttpRequest mutatedRequest = exchange.getRequest().mutate()
                .header(authConfig.getUserTokenHeader(), token)
                .build();
        
        return chain.filter(exchange.mutate().request(mutatedRequest).build());
    }

    @Override
    public int getOrder() {
        // 认证过滤器优先级应在日志过滤器之后、主要业务逻辑过滤器之前
        return Ordered.HIGHEST_PRECEDENCE + 30;
    }
} 